﻿using DM_TOOLS.Tools.DBC.DBCCSV;
using DM_TOOLS.Tools.DBC.DBCMessage;
using DM_TOOLS.Tools.DBC.DBCTime;
using DM_TOOLS.Tools.GPSConvert;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DM_TOOLS.Tools.DBC.DBCProcess
{
    public static class DBCProcess
    {
        /// <summary>
        /// 利用dbc文件和gzcsv文件进行解析并输出每行报文的真实数据，列名改为小写
        /// </summary>
        /// <param name="dbcdir"></param>
        /// <param name="gzcsvpath"></param>
        /// <returns></returns>
        public static async Task<List<Dictionary<string, double>>?> ExtractCanSignalsAsync(string dbcdir,string gzcsvpath)
        {
            //获取dbc文件的FileInfo列表
            var dbclist= dbcdir.GetExtensionFiles("dbc");
            //存储解析后的数据字典
            var result = new List<Dictionary<string, double>>();
            //获取所有原始gzcsv的数据
            var gzcsvdata = await DBCCSVReader.ReadCompressedCsvAllAsync(gzcsvpath);
            // 🚀 建立索引，加快查找 (Device+CanID → 对应的数据列表)
            var groupedData = gzcsvdata
                .GroupBy(t => (t.Device, t.CanID))
                .ToDictionary(g => g.Key, g => g.ToList());
            //循环每一个dbc文件
            foreach (var dbc in dbclist)
            {
                //从文件名中提取下划线后的数字部分（如 can_0.dbc → 0）
                var dbcindex = dbc.Name.GetNumberAfterUnderscore();
                //如果是100，说明没有dbc文件或者dbc文件名有误
                if (dbcindex == 100)
                {
                    return null ;
                }
                //用于解析dbc文件的方法，返回Message[]?
                var dbcmessagelist =dbc.FullName.DBCMessageReader();
                //循环每一个message
                foreach (var mes in dbcmessagelist)
                {
                    //获取每一个message下的所有通道
                    var siglist = mes.Signals;
                   
                    //获取每一个message下的ID
                    var id =mes.ID;
                    // 🚀 直接用索引取，不再 Where/ToList
                    if (!groupedData.TryGetValue((dbcindex, id), out var gzcsvdataid))
                        continue;
                    //解析每一行报文
                    foreach (var row in gzcsvdataid)
                    {
                        var dict = new Dictionary<string, double>
                        {
                            ["time"] = row.TimeStamp.ParseTimestamp()
                        };
                        foreach (var sig in siglist)
                        {
                            double value = DbcParserLib.Packer.RxSignalUnpack(row.FrameData, sig);
                            dict[sig.Name.ToLower()] = value;
                        }
                        // 保存结果
                        result.Add(dict);
                    }

                }
            }
            return result ;
        }

        
        /// <summary>
        /// 异步加载指定目录下的所有 DBC 文件，返回每个文件对应的消息数组。
        /// </summary>
        /// <param name="dbcdir">包含多个 .dbc 文件的目录路径。</param>
        /// <returns>
        /// 返回字典，键为 DBC 文件索引（如 can_0.dbc → 0），值为该文件解析得到的消息数组。
        /// </returns>
        public static async Task<Dictionary<uint, DbcParserLib.Model.Message[]>> LoadAllDbcMessagesAsync(string dbcdir)
        {
            return await Task.Run(() =>
            {
                var result = new Dictionary<uint, DbcParserLib.Model.Message[]>();
                var dbclist = dbcdir.GetExtensionFiles("dbc");
                if(dbclist == null||dbclist.Length==0)
                {
                    throw new Exception("没有读取到任何DBC文件");
                }
                foreach (var dbc in dbclist)
                {
                    // 提取下划线后的数字部分（如 can_0.dbc → 0）
                    var dbcindex = dbc.Name.GetNumberAfterUnderscore();
                    if (dbcindex == 100)
                        continue; // 文件名不符合规则则跳过

                    // 解析 DBC 文件
                    var messages = dbc.FullName.DBCMessageReader();
                    if (messages != null && messages.Length > 0)
                    {
                        result[dbcindex] = messages;
                    }
                    else
                    {
                        throw new Exception("解析DBC消息字典为空");
                    }
                }

                return result;
            });
        }


        /// <summary>
        /// 异步加载指定目录下的所有 DBC 文件，返回每个文件对应的消息数组。
        /// </summary>
        /// <param name="dbcdir">包含多个 .dbc 文件的目录路径。</param>
        /// <returns>
        /// 返回字典，键为 (Device, Channel)，值为该文件解析得到的消息数组。
        /// </returns>
        public static async Task<Dictionary<(uint Device, uint Channel), DbcParserLib.Model.Message[]>> LoadDbcMessagesByDeviceAndChannelAsync(string dbcdir)
        {
            return await Task.Run(() =>
            {
                var result = new Dictionary<(uint, uint), DbcParserLib.Model.Message[]>();
                var dbclist = dbcdir.GetExtensionFiles("dbc");
                if (dbclist == null || dbclist.Length == 0)
                {
                    throw new Exception("没有读取到任何DBC文件");
                }

                foreach (var dbc in dbclist)
                {
                    // 使用正则表达式提取设备号和通道号
                    var (device, channel) = dbc.Name.GetDeviceAndChannel();
                    if (device == 100 || channel == 100)
                        continue; // 文件名不符合规则则跳过

                    // 解析 DBC 文件
                    var messages = dbc.FullName.DBCMessageReader();
                    if (messages != null && messages.Length > 0)
                    {
                        result[(device, channel)] = messages;
                    }
                    else
                    {
                        throw new Exception($"解析DBC文件 {dbc.Name} 时消息字典为空");
                    }
                }

                return result;
            });
        }



        /// <summary>
        /// 异步从压缩 CSV 报文文件中解析信号数据。
        /// </summary>
        /// <param name="dbcMessagesDict">已加载的 DBC 消息字典（来自 LoadAllDbcMessagesAsync）。</param>
        /// <param name="gzcsvpath">压缩 CSV 文件路径。</param>
        /// <returns>解析后的信号字典列表。</returns>
        public static async Task<List<Dictionary<string, double>>> ExtractCanSignalsAsync(
            Dictionary<uint, DbcParserLib.Model.Message[]> dbcMessagesDict,
            string gzcsvpath)
        {
            // 校验
            if (dbcMessagesDict == null || dbcMessagesDict.Count == 0)
                throw new ArgumentException("DBC消息字典为空");

            // 1️⃣ 读取压缩 CSV 数据
            var gzcsvdata = await DBCCSVReader.ReadCompressedCsvAllAsync(gzcsvpath);

            // 2️⃣ 建立索引 (Device + CanID → 对应数据列表)
            var groupedData = gzcsvdata
                .GroupBy(t => (t.Device, t.CanID))
                .ToDictionary(g => g.Key, g => g.ToList());

            // 3️⃣ 存储解析结果
            var result = new List<Dictionary<string, double>>();

            // 4️⃣ 按 DBC 文件解析对应 CAN 报文
            foreach (var kvp in dbcMessagesDict)
            {
                var dbcindex = kvp.Key;
                var dbcmessagelist = kvp.Value;

                foreach (var mes in dbcmessagelist)
                {
                    var siglist = mes.Signals;
                    var id = mes.ID;
                   
                    // 🚨 新增检测：siglist 为空时抛出异常
                    if (siglist == null || siglist.Count == 0)
                    {
                        throw new InvalidOperationException(
                            $"DBC 文件中报文 '{mes.Name}' (ID: {id}) 的信号列表为空，" +
                            "可能是 DBC 文件中存在多余空格或格式问题，请检查并重新导出。");
                    }
                    // 按 Device + ID 匹配 CSV 报文数据，按每个id和dbcindex来匹配，比如第一个是ID是548和deviceid是0，匹配出2641行数据
                    // 如果匹配不到，说明报文里面没有这个id的数据，可能是传感器线断了
                    if (!groupedData.TryGetValue((dbcindex, id), out var gzcsvdataid))
                    {
                        // ✅ 匹配不到，也生成一条 -1 的数据记录
                        var dict = new Dictionary<string, double>
                        {
                            ["time"] = -1 // 表示该报文未在 CSV 中出现
                        };

                        foreach (var sig in siglist)
                        {
                            dict[sig.Name.ToLowerInvariant()] = -1; // 无数据标记
                        }

                        result.Add(dict);
                        continue; // 跳到下一个报文
                    }
                    
                    // 逐行解码
                    foreach (var row in gzcsvdataid)
                    {
                        var dict = new Dictionary<string, double>
                        {
                            ["time"] = row.TimeStamp.ParseTimestamp()
                        };

                        foreach (var sig in siglist)
                        {
                            double value = DbcParserLib.Packer.RxSignalUnpack(row.FrameData, sig);
                            dict[sig.Name.ToLowerInvariant()] = value;
                        }

                        result.Add(dict);
                    }
                }
            }

            return result;
        }


        /// <summary>
        /// 异步从压缩 CSV 报文文件中解析信号数据（按设备和通道进行匹配）。
        /// </summary>
        /// <param name="dbcMessagesDict">已加载的 DBC 消息字典（来自 LoadDbcMessagesByDeviceAndChannelAsync）。</param>
        /// <param name="gzcsvpath">压缩 CSV 文件路径。</param>
        /// <returns>解析后的信号字典列表。</returns>
        public static async Task<List<Dictionary<string, double>>> ExtractCanSignalsAsync(
            Dictionary<(uint Device, uint Channel), DbcParserLib.Model.Message[]> dbcMessagesDict,
            string gzcsvpath)
        {
            // 校验 DBC 消息字典是否为空
            if (dbcMessagesDict == null || dbcMessagesDict.Count == 0)
                throw new ArgumentException("DBC消息字典为空");

            // 1️⃣ 读取压缩 CSV 数据
            var gzcsvdata = await DBCCSVReader.ReadCompressedCsvAllAsync(gzcsvpath);

            // 2️⃣ 建立索引 (Device + Channel + CanID → 对应数据列表)
            var groupedData = gzcsvdata
                .GroupBy(t => (t.Device, t.Channel, t.CanID))
                .ToDictionary(g => g.Key, g => g.ToList());

            // 3️⃣ 存储解析结果
            var result = new List<Dictionary<string, double>>();

            // 4️⃣ 按 DBC 文件解析对应 CAN 报文
            foreach (var kvp in dbcMessagesDict)
            {
                var (device, channel) = kvp.Key;
                var dbcmessagelist = kvp.Value;

                foreach (var mes in dbcmessagelist)
                {
                    var siglist = mes.Signals;
                    var id = mes.ID;

                    // 🚨 检查信号列表是否为空
                    if (siglist == null || siglist.Count == 0)
                    {
                        throw new InvalidOperationException(
                            $"DBC 文件中报文 '{mes.Name}' (ID: {id}) 的信号列表为空，" +
                            "可能是 DBC 文件中存在多余空格或格式问题，请检查并重新导出。");
                    }

                    // 按 Device + Channel + ID 匹配 CSV 报文数据
                    if (!groupedData.TryGetValue((device, channel, id), out var gzcsvdataid))
                    {
                        // ✅ 如果没有匹配到数据，生成一条 -1 的数据记录
                        var dict = new Dictionary<string, double>
                        {
                            ["time"] = -1 // 表示该报文未在 CSV 中出现
                        };

                        foreach (var sig in siglist)
                        {
                            dict[sig.Name.ToLowerInvariant()] = -1; // 无数据标记
                        }

                        result.Add(dict);
                        continue; // 跳到下一个报文
                    }

                    // 逐行解码
                    foreach (var row in gzcsvdataid)
                    {
                        var dict = new Dictionary<string, double>
                        {
                            ["time"] = row.TimeStamp.ParseTimestamp()
                        };

                        foreach (var sig in siglist)
                        {
                            double value = DbcParserLib.Packer.RxSignalUnpack(row.FrameData, sig);
                            dict[sig.Name.ToLowerInvariant()] = value;
                        }

                        result.Add(dict);
                    }
                }
            }

            return result;
        }


        /// <summary>
        /// 异步从已解析的 CAN 数据中批量筛选多个信号的时间序列，会把目标信号名统一转为小写。
        /// </summary>
        /// <param name="parsedData">
        /// 已解析的完整 CAN 数据列表，每一行是 Dictionary&lt;string,double&gt;，
        /// 键为信号名或 "time"，值为对应数值。
        /// </param>
        /// <param name="targetSignalNames">
        /// 要提取的信号名称列表，例如 ["EngineSpeed", "VehicleSpeed"]。
        /// </param>
        /// <returns>
        /// 返回一个字典，键为信号名，值为该信号的时间序列 List&lt;(Time, Value)&gt;。
        /// </returns>
        /// <remarks>
        /// 异步方法通过 Task.Run 在后台线程执行，提高大数据量场景下的响应性。
        /// 每行缺失的信号会自动跳过。
        /// </remarks>
        public static async Task<Dictionary<string, List<(double Time, double Value)>>> FilterMultipleSignalSeriesAsync(List<Dictionary<string, double>> parsedData,List<string> targetSignalNames)
        {
            return await Task.Run(() =>
            {
                // 所有目标信号统一转为小写
                var lowerTargetNames = targetSignalNames
                    .Select(n => n.ToLowerInvariant())
                    .ToList();
                // 初始化结果字典
                var result = new Dictionary<string, List<(double Time, double Value)>>();

                // 为每个目标信号预分配 List 容量，减少动态扩容
                foreach (var name in lowerTargetNames)
                {
                    result[name] = new List<(double Time, double Value)>(parsedData.Count);
                }

                // 遍历每一行数据
                foreach (var row in parsedData)
                {
                    // 获取时间戳
                    if (!row.TryGetValue("time", out var time))
                        continue;

                    // 遍历每个目标信号
                    foreach (var name in lowerTargetNames)
                    {
                        if (row.TryGetValue(name, out var value))
                        {
                            result[name].Add((time, value));
                        }
                    }
                }
                return result;
            });
        }

        /// <summary>
        /// 异步对每个通道的时间序列按时间升序排序。
        /// </summary>
        /// <param name="signalSeries">
        /// 输入的信号字典，键为信号名，值为时间序列 List&lt;(Time, Value)&gt;。
        /// </param>
        /// <returns>
        /// 返回排序后的信号字典。
        /// </returns>
        public static async Task<Dictionary<string, List<(double Time, double Value)>>> SortSignalSeriesByTimeAsync(Dictionary<string, List<(double Time, double Value)>> signalSeries)
        {
            return await Task.Run(() =>
            {
                var sorted = new Dictionary<string, List<(double Time, double Value)>>();

                foreach (var kvp in signalSeries)
                {
                    // 按时间升序排序
                    var ordered = kvp.Value
                        .OrderBy(p => p.Time)
                        .ToList();

                    sorted[kvp.Key] = ordered;
                }

                return sorted;
            });
        }

        /// <summary>
        /// 异步对每个通道的时间序列进行等间隔降采样，仅保留指定数量的点，另添加了经纬度信号并进行了修正，只取第一个和最后一个数据。
        /// </summary>
        /// <param name="signalSeries">
        /// 输入的信号字典，键为信号名，值为时间序列 List&lt;(Time, Value)&gt;。
        /// </param>
        /// <param name="targetCount">
        /// 目标采样点数（默认 128）。
        /// </param>
        /// <returns>
        /// 返回降采样后的信号字典。
        /// </returns>
        public static async Task<Dictionary<string, List<(double Time, double Value)>>> DownsampleSignalSeriesAsync(Dictionary<string, List<(double Time, double Value)>> signalSeries, int targetCount = 128)
        {
            return await Task.Run(() =>
            {
                var downsampled = new Dictionary<string, List<(double Time, double Value)>>();

                foreach (var kvp in signalSeries)
                {
                    var key = kvp.Key.ToLower(); // 统一小写以便判断
                    var data = kvp.Value;
                    int n = data.Count;
                    // ✅ 如果 key 包含 lat 或 lon，仅取首尾两个点
                    if (key.Contains("lat") || key.Contains("lon"))
                    {
                        var gpssample = new List<(double Time, double Value)>
                        {
                            data.First(),
                            data.Last()
                        };
                        downsampled[kvp.Key] = gpssample;
                        continue;
                    }
                    // 如果数据点少于目标数量，则无需降采样
                    if (n <= targetCount)
                    {
                        downsampled[kvp.Key] = new List<(double Time, double Value)>(data);
                        continue;
                    }

                    // 等间隔采样
                    var sampled = new List<(double Time, double Value)>(targetCount);
                    double step = (double)(n - 1) / (targetCount - 1);

                    for (int i = 0; i < targetCount; i++)
                    {
                        int index = (int)Math.Round(i * step);
                        if (index >= n) index = n - 1;
                        sampled.Add(data[index]);
                    }

                    downsampled[kvp.Key] = sampled;
                }
                // ✅ 进行 GPS 坐标转换逻辑
                if (downsampled.ContainsKey("lat") && downsampled.ContainsKey("lon"))
                {
                    var latList = downsampled["lat"];
                    var lonList = downsampled["lon"];

                    if (latList.Count >= 2 && lonList.Count >= 2)
                    {
                        double lat1 = latList.First().Value;
                        double lon1 = lonList.First().Value;
                        double lat2 = latList.Last().Value;
                        double lon2 = lonList.Last().Value;

                        // 调用坐标转换函数
                        var newgps1 = GpsCoordinate.GpsToNewGps(lat1, lon1, 6);
                        var newgps2 = GpsCoordinate.GpsToNewGps(lat2, lon2, 6);

                        // 替换 lat 与 lon 的数据（保留原时间）
                        downsampled["lat"] = new List<(double Time, double Value)>
                        {
                            (latList.First().Time, newgps1._wgLat),
                            (latList.Last().Time, newgps2._wgLat)
                        };

                        downsampled["lon"] = new List<(double Time, double Value)>
                        {
                            (lonList.First().Time, newgps1._wgLon),
                            (lonList.Last().Time, newgps2._wgLon)
                        };
                    }
                }
                return downsampled;
            });
        }

        public static async Task ResampleAndSaveCsv(List<Dictionary<string, double>>? result,string outputCsvPath,double sampleRateHz = 500.0)
        {
            double dt = 1.0 / sampleRateHz; // 0.002s
            // 1. 通道名列表，去掉time
            var channelNames = result.SelectMany(r => r.Keys)
                                     .Distinct()
                                     .Where(k => k != "time")
                                     .OrderBy(k => k)
                                     .ToList();

            // 2. 时间范围，这里由于前面有time=-1的情况，所以需要排除
            double minTime = result.Where(r => r.ContainsKey("time") && r["time"] >= 0).Min(r => r["time"]);
            double maxTime = result.Where(r => r.ContainsKey("time") && r["time"] >= 0).Max(r => r["time"]);
            double duration = maxTime - minTime;

            // 3. 目标时间轴（从0开始）
            int sampleCount = (int)Math.Floor(duration / dt) + 1;
            var targetTimes = Enumerable.Range(0, sampleCount)
                                        .Select(i => i * dt)
                                        .ToList();

            // 4. 构建每个通道的原始序列要把time=-1的情况排除，time=-1的字典里的value会变成list的count为0
            var channelSeries = channelNames.ToDictionary(
                name => name,
                name => result.Where(r => r.ContainsKey(name) && r.ContainsKey("time") && r["time"] >= 0)
                              .Select(r => (Time: r["time"] - minTime, Value: r[name]))
                              .OrderBy(t => t.Time)
                              .ToList()
            );

            // 5. 重采样
            using var writer = new StreamWriter(outputCsvPath, false, Encoding.UTF8);
            // 写表头
            await writer.WriteLineAsync("time," + string.Join(",", channelNames));

            foreach (var t in targetTimes)
            {
                var row = new List<string> { t.ToString("F3", CultureInfo.InvariantCulture) };

                foreach (var name in channelNames)
                {
                    var series = channelSeries[name];
                    if (series.Count == 0)
                    {
                        row.Add("-1"); // 没有值
                        continue;
                    }

                    // 二分查找目标时间 t 的插入点
                    int left = 0, right = series.Count - 1;
                    while (left <= right)
                    {
                        int mid = (left + right) / 2;
                        if (series[mid].Time < t)
                            left = mid + 1;
                        else
                            right = mid - 1;
                    }

                    // left 是第一个 >= t 的位置
                    var next = (left < series.Count) ? series[left] : series[^1];
                    var prev = (left > 0) ? series[left - 1] : series[0];

                    double value;
                    if (Math.Abs(prev.Time - next.Time) < 1e-9) // 相同点
                        value = prev.Value;
                    else if (prev.Time <= t && next.Time >= t) // 插值
                    {
                        double alpha = (t - prev.Time) / (next.Time - prev.Time);
                        value = prev.Value + alpha * (next.Value - prev.Value);
                    }
                    else if (t < series[0].Time) // 超前
                        value = series[0].Value;
                    else // 超后
                        value = series[^1].Value;

                    row.Add(value.ToString("F6", CultureInfo.InvariantCulture));
                }

                await writer.WriteLineAsync(string.Join(",", row));
            }
        }


        /// <summary>
        /// 异步计算速度信号的平均速度和总里程
        /// </summary>
        /// <param name="data">包含 "spd" 信号的字典，Key 为信号名，Value 为 (Time, Value) 列表</param>
        /// <returns>返回一个元组：— AvgSpeed: 平均速度 (单位 km/h) — DistanceKm: 总里程(单位 km)</returns>
        /// <exception cref="ArgumentException"></exception>
        public static async Task<(double AvgSpeed, double DistanceKm)> CalculateSpeedStatsAsync(Dictionary<string, List<(double Time, double Value)>> data)
        {
            return await Task.Run(() =>
            {
                // 查找字典中所有键名包含 "spd" 的数据
                var speedSeries = data
                    .Where(kv => kv.Key.Contains("spd", StringComparison.OrdinalIgnoreCase) && kv.Value != null && kv.Value.Count >= 2)
                    .Select(kv => kv.Value)
                    .FirstOrDefault();
                if (speedSeries == null)
                    throw new ArgumentException("字典中没有有效的速度数据。");
                //if (!data.TryGetValue("spd", out var speedSeries) || speedSeries == null || speedSeries.Count < 2)
                //    throw new ArgumentException("字典中没有有效的 'spd' 数据。");

                // 按时间排序
                var sorted = speedSeries.OrderBy(x => x.Time).ToList();
                // 平均速度
                double avgSpeed = sorted.Average(x => x.Value);
                // 总时长（秒）
                double duration = sorted.Last().Time - sorted.First().Time;
                // 总里程（假设 Time 单位秒，速度单位 km/h）
                double distance = 0;
                distance = Math.Round(avgSpeed / 3600 * duration, 2);
                //for (int i = 1; i < sorted.Count; i++)
                //{
                //    double v1 = sorted[i - 1].Value;
                //    double v2 = sorted[i].Value;
                //    double t1 = sorted[i - 1].Time;
                //    double t2 = sorted[i].Time;

                //    double deltaT = t2 - t1;
                //    if (deltaT <= 0) continue;

                //    // 梯形积分计算里程
                //    distance += ((v1 + v2) / 2.0) * deltaT / 3600.0;
                //}
                return (avgSpeed, distance);
            });
        }
    }
}
